package com.catazy.mvvmlib.ext.download

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.ResponseBody
import java.io.File
import java.io.RandomAccessFile
import java.nio.channels.FileChannel

/**
 * 作者: Tao
 * 日期: 2024/1/30
 * 时间: 11:40
 * 描述: 下载文件到本地工具类
 */
object FileTool {
    //GB常量
    private const val GB = 1024 * 1024 * 1024

    //MB常量
    private const val MB = 1024 * 1024

    //KB常量
    private const val KB = 1024

    /**
     * 下载文件到本地
     * @param key String 文件url
     * @param savePath String 保存路径
     * @param saveName String 保存名称
     * @param currentLength Long 当前下载长度
     * @param responseBody ResponseBody 响应体
     * @param loadListener OnDownLoadListener 下载监听
     */
    suspend fun downToFile(
        key: String,
        savePath: String,
        saveName: String,
        currentLength: Long,
        responseBody: ResponseBody,
        loadListener: OnDownloadListener
    ) {
        val filePath = getFilePath(savePath, saveName)
        try {
            if (filePath == null) {
                withContext(Dispatchers.Main) {
                    loadListener.onDownloadError(key, Throwable("mkdirs file [$savePath]  error"))
                }
                DownLoadPool.remove(key)
                return
            }
            //保存到文件
            saveToFile(currentLength, responseBody, filePath, key, loadListener)
        } catch (throwable: Throwable) {
            withContext(Dispatchers.Main) {
                loadListener.onDownloadError(key, throwable)
            }
            DownLoadPool.remove(key)
        }
    }

    /**
     * 保存到文件
     * @param currentLength Long 当前下载长度
     * @param responseBody ResponseBody 响应体
     * @param filePath String 文件路径
     * @param key String 文件url
     * @param loadListener OnDownLoadListener 下载监听
     */

    private suspend fun saveToFile(
        currentLength: Long,
        responseBody: ResponseBody,
        filePath: String,
        key: String,
        loadListener: OnDownloadListener
    ) {
        val fileLength =
            getFileLength(currentLength, responseBody)
        val inputStream = responseBody.byteStream()
        val accessFile = RandomAccessFile(File(filePath), "rwd")
        val channel = accessFile.channel
        val mappedBuffer = channel.map(
            FileChannel.MapMode.READ_WRITE,
            currentLength,
            fileLength - currentLength
        )
        val buffer = ByteArray(1024 * 4)
        var len = 0
        var lastProgress = 0
        var currentSaveLength = currentLength //当前的长度

        while (inputStream.read(buffer).also { len = it } != -1) {
            mappedBuffer.put(buffer, 0, len)
            currentSaveLength += len

            val progress = (currentSaveLength.toFloat() / fileLength * 100).toInt() // 计算百分比
            if (lastProgress != progress) {
                lastProgress = progress
                //记录已经下载的长度
                ShareDownLoadUtil.putLong(key, currentSaveLength)
                withContext(Dispatchers.Main) {
                    loadListener.onUpdate(
                        key,
                        progress,
                        currentSaveLength,
                        fileLength,
                        currentSaveLength == fileLength
                    )
                }

                if (currentSaveLength == fileLength) {
                    withContext(Dispatchers.Main) {
                        loadListener.onDownloadSuccess(key, filePath, fileLength)
                    }
                    DownLoadPool.remove(key)
                }
            }
        }

        inputStream.close()
        accessFile.close()
        channel.close()
    }

    /**
     * 获取文件长度
     * @param currentLength Long 当前长度
     * @param responseBody ResponseBody 响应体
     * @return Long 文件长度
     */
    private fun getFileLength(currentLength: Long, responseBody: ResponseBody): Long {
        return if (currentLength > 0) {
            currentLength + responseBody.contentLength()
        } else {
            responseBody.contentLength()
        }
    }

    /**
     * 获取文件路径
     * @param savePath String 保存路径
     * @param saveName String 保存名称
     * @return String? 文件路径
     */
    private fun getFilePath(savePath: String, saveName: String): String? {
        if (!createFile(savePath)) {
            return null
        }
        return "$savePath/$saveName"
    }

    /**
     * 创建文件
     * @param savePath String 保存路径
     * @return Boolean 创建结果
     */
    private fun createFile(savePath: String): Boolean {
        val file = File(savePath)
        if (!file.exists()) {
            return file.mkdirs()
        }
        return true
    }
}